import { HttpClient } from '@angular/common/http';
import { Component, DoCheck, ElementRef, EventEmitter, Input, OnInit, Output, Renderer2, ViewChild } from '@angular/core';
import { TreeNode } from 'primeng/api';

@Component({
  selector: 'oy-r-dropdown-tree',
  templateUrl: './r-dropdown-tree.component.html',
  styleUrls: ['./r-dropdown-tree.component.scss']
})

/**
 * 描述：公共模块：树形结构下拉框实时获取数据，并初始化回填数据ts
 * 公司：xxx有限公司
 * 版权：xxx2020
 * 作者：oyjl
 * 时间：2020-04-03 10:20:40
 */
export class RDropdownTreeComponent implements OnInit, DoCheck {

  public trees: TreeNode[]; // 树形结构数据
  public selectedTrees: any; // 选中的树形结构数据
  public showTree: boolean; // 是否显示树形结构
  public selectorTreeStr: string; // 选中的树节点
  public treeStatus: boolean; // 当前被点击元素状态，true为激活，false为取消
  @Input() treeConfig: TreeConfig;
  @Input() selectedTreeNodes; // 选中的树形结构数据
  @Output() selectedTreeNodesChange: EventEmitter<any> = new EventEmitter<any>();
  @ViewChild('inputIcon') inputIcon;
  @Input() isTileData = false; // 是否平铺结构的list数据，true表示list结构需要重新根据父子节点构建树形结构
  constructor(
    private http: HttpClient,
    private elementRef: ElementRef,
    private renderer: Renderer2) { }

  ngOnInit() {
    const el = this.elementRef.nativeElement.querySelector('.tree-main');
    this.renderer.listen('document', 'click', (e) => {
      if (!this.treeStatus) {
        this.showTree = false;
        this.inputIcon.nativeElement.classList.remove('pi-chevron-up');
        this.inputIcon.nativeElement.classList.add('pi-chevron-down');
      }
      this.treeStatus = false;
    });
    this.renderer.listen(el, 'click', (e) => {
      this.treeStatus = true;
    });
  }

  ngDoCheck(): void {
    this.fileSelectedHandle(this.outSwitchInner(this.selectedTreeNodes));
  }

  /**
   * 描述：点击下拉框，下发请求获取数据
   * 作者：oyjl
   * 时间：2020-04-03 11:23:36
   */
  public dropdownTreeHandle(inputIcon: HTMLElement) {
    this.getTreeData(inputIcon);
  }

  /**
   * 描述：下发后端请求,获取数据,并修改右侧图标样式
   * 作者：oyjl
   * 时间：2020-04-03 13:05:28
   */
  private getTreeData(inputIcon: HTMLElement) {
    if (inputIcon.classList.contains('pi-chevron-down')) {
      inputIcon.classList.toggle('pi-spin');
      inputIcon.classList.toggle('pi-spinner');
      const server = this.treeConfig.server;
      this.http.request(server.method, server.url, { body: server.req }).subscribe((req: any) => {
        if (!server.status) {
          server.status = 'operateSuccess';
        }
        if (req[server.status]) {
          try {
            if (!server.resKey) {
              server.resKey = 'pageModel.rows';
            }
            const keys = server.resKey.split('.');
            for (const key of keys) {
              req = req[key];
            }
            let newReq = req;
            if (this.isTileData) {
              // 去掉重复菜单栏
              const func = this.funcHandle(req, this.treeConfig.key);
              // 数据转换成树形结构
              newReq = this.arraySwitchTree(func, this.treeConfig.key, this.treeConfig.parentKey);
            }
            this.trees = this.conversion(newReq || [], this.treeConfig.node);
            if (this.treeConfig.selectionMode === 'single' && this.treeConfig.singleFirstNode.hasOwnProperty('key')) {
              this.trees.unshift(this.treeConfig.singleFirstNode);
            }
            this.selectedTrees = this.treeBackfill(this.trees, this.outSwitchInner(this.selectedTreeNodes)).array || [];
            this.fileSelectedHandle(this.selectedTrees);
            inputIcon.classList.toggle('pi-spin');
            inputIcon.classList.toggle('pi-spinner');
            inputIcon.classList.toggle('pi-chevron-up');
            inputIcon.classList.toggle('pi-chevron-down');
            this.showTree = true;
          } catch (error) {
            console.error(error);
          }
        }
      }, error => {
        console.error(error);
      });
    } else if (inputIcon.classList.contains('pi-chevron-up')) {
      this.showTree = false;
      inputIcon.classList.toggle('pi-chevron-up');
      inputIcon.classList.toggle('pi-chevron-down');
    } else { }
  }

  /**
   * 描述：递归处理树形结构数据
   * 作者：oyjl
   * 时间：2020-04-03 13:05:28
   */
  private conversion(data, field): Array<object> {
    const arrRef = [];
    // tslint:disable-next-line:prefer-for-of
    for (let i = 0; i < data.length; i++) {
      let res = {};
      if (field.children && data[i][field.children] && data[i][field.children].length > 0) {
        res = {
          key: data[i][field.value], // 节点的唯一值，如果没有指定，在过滤选中的时候会出现堆栈溢出错误
          icon: !(!this.treeConfig.isOnlyChooseLast && (this.treeConfig.valRangeStatus ? this.selectableItem(data[i]) :
            this.unSelectableItem(data[i]))) && 'fa fa-minus-square',
          label: data[i][field.name],
          selectable: !this.treeConfig.isOnlyChooseLast && (this.treeConfig.valRangeStatus ? this.selectableItem(data[i]) :
            this.unSelectableItem(data[i])),
          data: data[i],
          expanded: this.treeConfig.expanded,
          children: this.conversion(data[i][field.children], field)
        };
        arrRef.push(res);
      } else {
        res = {
          key: data[i][field.value],
          icon: !((this.treeConfig.valRangeStatus ? this.selectableItem(data[i]) : this.unSelectableItem(data[i]))) && 'fa fa-minus-square',
          label: data[i][field.name],
          selectable: (this.treeConfig.valRangeStatus ? this.selectableItem(data[i]) : this.unSelectableItem(data[i])),
          data: data[i],
          expanded: this.treeConfig.expanded
        };
        arrRef.push(res);
      }
    }
    return arrRef;
  }

  /**
   * 描述：树形结构回填处理
   * 作者：oyjl
   * 时间：2020-04-03 16:22:46
   */
  private treeBackfill(tree, data = []) {
    let array = [];
    for (const i of tree || []) {
      if (i.children !== undefined) {
        for (const j of data) {
          if (j.key === i.key) {
            array.push(i);
          }
        }
        const list = this.treeBackfill(i.children, data);
        array = array.concat(list.array);
      } else {
        for (const j of data) {
          if (j.key === i.key) {
            array.push(i);
          }
        }
      }
    }
    return { array };
  }

  /**
   * 描述：树形节点是否选中处理，默认可以选中
   * 作者：oyjl
   * 时间：2020-04-03 13:05:28
   */
  private selectableItem(data): boolean {
    if (this.treeConfig.valRange) {
      for (const key of Object.keys(this.treeConfig.valRange)) {
        if (this.treeConfig.valRange[key].includes(data[key])) {
          return false;
        }
        // tslint:disable-next-line:no-unused-expression
        this.treeConfig.valRange[key];
      }
    }
    return true;
  }

  /**
   * 描述：树形节点是否选中处理，默认可以选中，与上一方法相反，取没有属性或者没有找到匹配值
   * 作者：oyjl
   * 时间：2020-04-08 14:38:48
   */
  private unSelectableItem(data): boolean {
    if (this.treeConfig.valRange) {
      for (const key of Object.keys(this.treeConfig.valRange)) {
        if (this.treeConfig.valRange[key].includes(data[key]) || (!this.treeConfig.valRange[key] && data[key])) {
          return true;
        }
        // tslint:disable-next-line:no-unused-expression
        this.treeConfig.valRange[key];
      }
    }
    return false;
  }

  /**
   * 描述：选中的树形结构字符串拼接
   * 作者：oyjl
   * 时间：2020-04-03 16:02:48
   */
  private fileSelectedHandle(nodes) {
    this.selectorTreeStr = '';
    let temp = [];
    if (nodes && nodes instanceof Array) {
      if (nodes.length > 3) {
        this.selectorTreeStr = nodes.length + '项被选中';
        return;
      } else {
        nodes.forEach((element: TreeNode) => {
          temp.push(element.label || element[this.treeConfig.node.name]);
        });
      }
    } else if (nodes) {
      temp = [nodes.label || nodes[this.treeConfig.node.name]];
    } else { }
    this.selectorTreeStr = temp.join('，');
  }

  /**
   * 描述：选中节点处理
   * 作者：oyjl
   * 时间：2020-04-03 16:04:48
   */
  public onNodeSelect(event) {
    this.fileSelectedHandle(this.selectedTrees);
    this.selectedTreeNodesChange.emit(this.selectedTrees);
    if (this.treeConfig.nodeClickFunc) {
      this.treeConfig.nodeClickFunc(this.selectedTrees);
    }
  }

  /**
   * 描述：取消选中节点处理
   * 作者：oyjl
   * 时间：2020-04-03 16:04:48
   */
  public onNodeUnselect(event) {
    this.fileSelectedHandle(this.selectedTrees);
    this.selectedTreeNodesChange.emit(this.selectedTrees);
    if (this.treeConfig.nodeClickFunc) {
      this.treeConfig.nodeClickFunc(this.selectedTrees);
    }
  }

  /** 描述：外部节点转换成内部节点对象
   * 作者：oyjl
   * 时间：2020-04-07 09:32:28
   */
  private outSwitchInner(data) {
    const innerNode = [];
    if (data) {
      if (data instanceof Array) {
        data.forEach(element => {
          innerNode.push({
            key: element.key || element[this.treeConfig.node.value],
            label: element.label || element[this.treeConfig.node.name],
          });
        });
      } else {
        innerNode.push({
          key: data.key || data[this.treeConfig.node.value],
          label: data.label || data[this.treeConfig.node.name],
        });
      }
    }
    return innerNode;
  }

  /**
   * 描述：将数组格式转换成树形格式
   * 入参：func为数组格式数据，key为主键ID，parentKey为父级ID
   * 出参：组装后的树形结构的数组
   * 作者：oyjl
   * 时间：2022-06-17 14:38:34
   */
  arraySwitchTree(func, key, parentKey): Array<any> {
    const funcTree: Array<any> = (function makeTreeData(arr, parentId) {
      const temp = [];
      // tslint:disable-next-line:prefer-for-of
      for (let i = 0; i < arr.length; i++) {
        if (arr[i][parentKey] === parentId) {
          temp.push(arr[i]);
          arr[i].children = makeTreeData(func, arr[i][key]);
        }
      }
      return temp.length > 0 ? temp : undefined;
    })(func, undefined);
    return funcTree;
  }

  /**
   * 描述：根据主键去掉重复数据,接口数据
   * 入参：func权限数组，key每条数据唯一值
   * 出参：去重后的权限数组
   * 作者：oyjl
   * 时间：2022-06-17 15:44:34
   */
  funcHandle(func, key): Array<any> {
    const o = {};
    func.forEach((e: any) => {
      o[e[key]] = e;
    });
    const a = [];
    for (const i of Object.keys(o)) {
      if (o[i].type === 0) {
        a.push(o[i]);
      }
    }
    return a;
  }
}

/**
 * 描述：服务端配置接口
 * 作者：oyjl
 * 时间：2020-04-03 11:26:22
 */
interface Server {
  method: string; // 请求方法：POST,GET
  url: string; // 树形结构的url地址
  req: any; // 传递给后端的内容
  resKey?: string; // 获取后端返回的树形结构根节点key值
  status?: string; // 下发接口成功与否状态
}

/**
 * 描述：树形节点映射字段接口
 * 作者：oyjl
 * 时间：2020-04-03 16:31:42
 */
interface Node {
  name: string; // 映射名称
  value: string; // 映射ID
  children: string; // 映射叶节点
}

/**
 * 描述：树形结构配置
 * 作者：oyjl
 * 时间：2020-04-03 13:26:47
 */
export class TreeConfig {
  server: Server; // 树形后端配置
  node?: Node; // 树形节点映射，默认{ name: 'departmentName', value: 'departmentId', children: 'departmentList' }
  selectionMode?: string; // 选中的模式：single（单选）、multiple（多选）、checkbox（多选框），默认single
  singleFirstNode?: any; // 单选模式的首节点，默认{key：-1，label:'无'},
  appendTo?: string; // 附加覆盖目标元素：current（当前元素）、body（body元素），默认为current
  filter?: boolean; // 是否过滤，默认不过滤
  filterBy?: string; // 过滤的字段
  filterMode?: string; // 过滤模式：lenient（宽宏模式），strict（严格模式），默认宽宏模式
  filterPlaceholder?: string; // 过滤数据的时候，默认展示的文本
  valRangeStatus?: boolean; // 范围状态，true表示范围内不可用，false表示范围内可用，默认为true
  valRange?: object; // 范围：格式{key:[1,2,3]}，与valRangeStatus一同处理选中
  expanded?: boolean; // 是否展开所以子节点,默认为false
  propagateSelectionUp?: boolean; // 复选框是否传播到祖先节点，默认true
  propagateSelectionDown?: boolean; // 复选框是否传播到后代节点，默认true
  width?: string; // 下拉选择框宽度
  nodeClickFunc?: (argv) => void; // 节点操作回调函数,用于多个树形结构点击节点以后联动操作
  isOnlyChooseLast?: boolean; // 是否仅仅允许选中最末级，true表示只能选中最末级，false表示树所有节点均可以选择，默认为true
  disabled?: boolean; // 下拉树是否可用，true不可用，false可用，默认false
  key?: string; // 主键，主要用于数组组树处理
  parentKey?: string; // 外键，主要用于数组组树处理
  constructor(data: TreeConfig) {
    this.server = data.server;
    this.node = data.node || { name: 'departmentName', value: 'departmentId', children: 'departmentList' };
    this.selectionMode = data.selectionMode || 'single';
    this.singleFirstNode = data.singleFirstNode || { key: -1, label: '无' };
    this.appendTo = data.appendTo === 'body' ? 'body' : 'current';
    this.filter = data.filter || false;
    this.filterBy = data.filterBy || 'label';
    this.filterMode = data.filterMode || 'lenient';
    this.filterPlaceholder = data.filterPlaceholder || '请输入查询条件';
    this.valRangeStatus = data.valRangeStatus === false ? false : true;
    this.valRange = data.valRange || {};
    this.expanded = data.expanded || false;
    this.propagateSelectionUp = data.propagateSelectionUp === false ? false : true;
    this.propagateSelectionDown = data.propagateSelectionDown === false ? false : true;
    this.width = data.width || 'fit-content';
    this.nodeClickFunc = data.nodeClickFunc;
    this.isOnlyChooseLast = data.isOnlyChooseLast === false ? false : true;
    this.disabled = data.disabled || false;
    this.key = data.key || 'seq';
    this.parentKey = data.parentKey || 'parentSeq';
  }
}
